package Mongo

import (
	"context"
	"fmt"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/bson/primitive"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
	"reflect"
)

func GetId(doc map[string]interface{}) interface{} {
	return doc["_id"]
}

// InsertOne 插入一条记录
func InsertOne(collection string, document map[string]interface{}) error {
	c := DB.db.Collection(collection)

	delete(document, "_id")
	result, err := c.InsertOne(context.TODO(), document)
	if err != nil {
		return err
	}

	document["_id"] = result.InsertedID.(primitive.ObjectID)
	return err
}

// InsertMany 插入多条记录
func InsertMany(collection string, documents []map[string]interface{}) error {
	c := DB.db.Collection(collection)

	if len(documents) == 0 {
		return nil
	}

	list := make([]interface{}, 0)
	for _, v := range documents {
		delete(v, "_id")
		list = append(list, v)
	}

	result, err := c.InsertMany(context.TODO(), list)
	if err != nil {
		return err
	}

	for k, v := range result.InsertedIDs {
		documents[k]["_id"] = v.(primitive.ObjectID)
	}

	return err
}

// FindOne 查找一条记录
// filter 过滤条件，范例：bson.M{"title": "Inception"}
func FindOne(collection string, filter interface{}) (map[string]interface{}, error) {
	c := DB.db.Collection(collection)

	record := c.FindOne(context.TODO(), filter)

	document := make(map[string]interface{})
	err := record.Decode(document)
	if err != nil {
		if err == mongo.ErrNoDocuments {
			return nil, nil
		}
		return nil, err
	}

	return document, err
}

// Find 查找多条记录
// filter 过滤条件，范例：bson.M{"year": bson.M{"$gt": 2000}}
func Find(collection string, filter interface{}, opts ...*options.FindOptions) ([]map[string]interface{}, error) {
	docs := make([]map[string]interface{}, 0)

	c := DB.db.Collection(collection)

	// 查询多个文档
	cursor, err := c.Find(context.TODO(), filter, opts...)
	if err != nil {
		return docs, err
	}
	defer cursor.Close(context.TODO())

	if err = cursor.All(context.TODO(), &docs); err != nil {
		return docs, err
	}

	return docs, nil
}

// FindPage 分页查询
// filter 过滤条件，范例：bson.M{}
// sort 排序方式，范例：bson.D{{"_id", -1}})
func FindPage(collection string, filter interface{}, sort interface{}, pageNum, pageSize int) ([]map[string]interface{}, error) {
	// 计算跳过的文档数量
	skip := (pageNum - 1) * pageSize

	// 设置查询选项
	opts := options.Find().SetSort(sort).
		SetSkip(int64(skip)).
		SetLimit(int64(pageSize))

	return Find(collection, filter, opts)
}

func FindCount(collection string, filter interface{}) (int64, error) {
	c := DB.db.Collection(collection)
	return c.CountDocuments(context.TODO(), filter)
}

// FindCountEstimated 大概估算全表的数据
// 说明：此时不会扫描各个分片的数据
func FindCountEstimated(collection string) (int64, error) {
	c := DB.db.Collection(collection)
	return c.EstimatedDocumentCount(context.TODO())
}

func FindKeys(collection string, fieldName string, keys interface{}) (existingKeys []interface{}, missingKeys []interface{}, err error) {
	if reflect.TypeOf(keys).Kind() != reflect.Slice {
		return nil, nil, fmt.Errorf("输入的keys不是一个Slice")
	}

	c := DB.db.Collection(collection)

	// 获取在给定keys中实际存在的keys
	existingKeysInterface, err := c.Distinct(context.TODO(), fieldName, bson.M{fieldName: bson.M{"$in": keys}})
	if err != nil {
		return nil, nil, fmt.Errorf("distinct query failed: %v", err)
	}

	// 转换为字符串切片
	existingSet := make(map[interface{}]bool)
	for _, key := range existingKeysInterface {
		if strKey, ok := key.(string); ok {
			existingSet[strKey] = true
			existingKeys = append(existingKeys, strKey)
		}
	}

	// 找出不存在的keys
	v := reflect.ValueOf(keys)
	for index := 0; index < v.Len(); index++ {
		key := v.Index(index).Interface()
		if !existingSet[key] {
			missingKeys = append(missingKeys, key)
		}
	}

	return existingKeys, missingKeys, nil
}

// UpdateOne 更新一条记录
// filter 过滤条件，范例：bson.M{"title": "Inception"}
// update 更新操作，范例：bson.M{"$set": bson.M{"rating": 9.0}}
func UpdateOne(collection string, filter interface{}, update interface{}) (*mongo.UpdateResult, error) {
	c := DB.db.Collection(collection)
	return c.UpdateOne(context.TODO(), filter, update)
}

func UpdateMany(collection string, filter interface{}, update interface{}) (*mongo.UpdateResult, error) {
	c := DB.db.Collection(collection)
	return c.UpdateMany(context.TODO(), filter, update)
}

func UpdateByID(collection string, id interface{}, update interface{}) (*mongo.UpdateResult, error) {
	c := DB.db.Collection(collection)
	return c.UpdateByID(context.TODO(), id, update)
}

// DeleteOne 删除一条记录
// filter 过滤条件，范例：bson.M{"title": "Inception"}
func DeleteOne(collection string, filter interface{}) (*mongo.DeleteResult, error) {
	c := DB.db.Collection(collection)
	return c.DeleteOne(context.TODO(), filter)
}

func DeleteById(collection string, id interface{}) (*mongo.DeleteResult, error) {
	c := DB.db.Collection(collection)
	return c.DeleteOne(context.TODO(), bson.D{{"_id", id}})
}

func ListIndex(collection string) ([]bson.M, error) {
	c := DB.db.Collection(collection)

	// 获取索引游标
	cursor, err := c.Indexes().List(context.TODO())
	if err != nil {
		return nil, err
	}
	defer cursor.Close(context.TODO())

	// 存储索引结果
	var indexes []bson.M
	if err = cursor.All(context.TODO(), &indexes); err != nil {
		return nil, err
	}

	return indexes, nil
}

// CreateIndex 创建索引
func CreateIndex(collection string, indexModel mongo.IndexModel) (string, error) {
	c := DB.db.Collection(collection)
	return c.Indexes().CreateOne(context.TODO(), indexModel)
}

// CreateEasyIndex 创建简单索引
// key 索引的位置
// unique 是否为唯一索引
func CreateEasyIndex(collection string, key string, unique bool) (string, error) {
	// 创建索引模型
	indexModel := mongo.IndexModel{
		Keys: bson.D{
			{Key: key, Value: 1}, // 1表示升序索引，-1表示降序索引
		},
		Options: options.Index().SetUnique(unique), // 设置索引选项，这里设置为唯一索引
	}

	return CreateIndex(collection, indexModel)
}

func Compact(collection string, force bool) error {
	// 执行 compact 命令
	cmd := bson.D{{"compact", collection}, {"force", true}}

	_, err := DB.db.RunCommand(context.TODO(), cmd).DecodeBytes()
	return err
}

func Aggregate(collection string, pipeline interface{}, opts ...*options.AggregateOptions) (*mongo.Cursor, error) {
	c := DB.db.Collection(collection)
	return c.Aggregate(context.TODO(), pipeline, opts...)
}

func Distinct(collection string, fieldName string, filter interface{}, opts ...*options.DistinctOptions) ([]interface{}, error) {
	c := DB.db.Collection(collection)
	return c.Distinct(context.TODO(), fieldName, filter, opts...)
}
